use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Instant;

use chrono::{Local, TimeZone};
use once_cell::sync::OnceCell;
use secp256k1::{PublicKey, Secp256k1, SecretKey, rand::rngs::OsRng};
use tracing_subscriber::{EnvFilter, FmtSubscriber, fmt::time::UtcTime};

static LAST_TIME: AtomicU64 = AtomicU64::new(0);

/// Get the current Unix timestamp in seconds, but with a monotonic clock.
///
/// std::time::SystemTime::now() 变小的可能原因：
///  系统时间发生回拨；
///  多线程输出乱序；
///  使用 .as_secs() 的精度截断造成边界跳动；
///  系统时钟受 NTP 或用户干预；
fn get_monotonic_sys_time_seconds() -> u64 {
    let now = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .expect("Time went backwards")
        .as_secs();

    let mut prev = LAST_TIME.load(Ordering::Relaxed);
    while now > prev {
        match LAST_TIME.compare_exchange(prev, now, Ordering::Relaxed, Ordering::Relaxed) {
            Ok(_) => return now,
            Err(v) => prev = v,
        }
    }

    prev
}

pub mod logged_mutex;
pub mod time_provider;

pub fn print_task_and_thread_id(task_name: &str) {
    let task_id = tokio::task::try_id();
    let thread_id = std::thread::current().id();
    println!(
        "[{}] Task ID: {:?}, Thread ID: {:?}",
        task_name, task_id, thread_id
    );
}

// pub fn bc_create_keypair() -> (SecretKey, PublicKey) {
//     let secp = Secp256k1::new();
//     let priv_key = secp256k1::SecretKey::new(&mut OsRng);
//     let public_key = PublicKey::from_secret_key(&secp, &priv_key);
//     (priv_key, public_key)
// }

pub fn generate_secp256k1_keypair() -> (SecretKey, PublicKey) {
    let secp = Secp256k1::new();
    secp.generate_keypair(&mut OsRng)
}

/// Converts a u32 representing seconds since the Unix epoch to a local time string.
///
/// # Arguments
/// * `seconds` - A u32 value representing seconds since January 1, 1970 (Unix epoch).
///
/// # Returns
/// A String containing the local date and time formatted as "YYYY-MM-DD HH:MM:SS".
pub fn seconds_to_local_time(seconds: u64) -> String {
    // Convert the u32 seconds to i64 (chrono uses i64 for timestamps)
    let ts = seconds as i64;
    // Create a DateTime in the local timezone using the provided timestamp.
    let local_dt = Local.timestamp_opt(ts, 0).unwrap();
    // Format the DateTime as a string.
    local_dt.format("%Y-%m-%d %H:%M:%S").to_string()
}

/// Get the current Unix timestamp
pub fn get_current_sys_time_seconds() -> u64 {
    get_monotonic_sys_time_seconds()
}

static LOGGER_INIT: OnceCell<()> = OnceCell::new();

pub fn init_logger_for_test(log_level: Option<tracing::Level>) {
    LOGGER_INIT.get_or_init(|| {
        let level = match log_level {
            None => tracing::Level::TRACE,
            Some(level) => level,
        };
        let time_format =
            time::format_description::parse("[hour]:[minute]:[second].[subsecond digits:3]")
                .expect("format string should be valid!");
        let timer = UtcTime::new(time_format);

        let filter = EnvFilter::new("info").add_directive("libp2p=warn".parse().unwrap());
        let subscriber = FmtSubscriber::builder()
            .with_env_filter(filter)
            .with_max_level(level)
            .with_timer(timer)
            .finish();

        tracing::subscriber::set_global_default(subscriber)
            .expect("Setting default subscriber failed");
    });
}

pub async fn execute_function_elapsed(
    prompt: &str,
    user_fn: impl std::future::Future<Output = ()>,
) {
    tracing::info!("Start execute `{}` ...", prompt);
    println!("Execute `{}` ...", prompt);
    let start = Instant::now();
    user_fn.await;
    let duration = start.elapsed();
    println!(
        "Time elapsed in execute `{}` is: {:?} ms\n",
        prompt,
        duration.as_millis()
    );
}

pub fn convert_to_temp_dir(prefix: &str) -> tempfile::TempDir {
    tempfile::Builder::new()
        .prefix(prefix)
        .tempdir()
        .unwrap_or_else(|_| panic!("Failed to create temporary path for the {}", prefix))
}

pub fn create_delay_task(
    deadline_secs: u64,
    mut shutdown_rx: tokio::sync::watch::Receiver<bool>,
) -> tokio::task::JoinHandle<()> {
    tokio::task::spawn(async move {
        let deadline = Instant::now() + tokio::time::Duration::from_secs(deadline_secs);
        loop {
            tokio::select! {
                _ = shutdown_rx.changed() => {
                    println!("[delay task] shutdown-signal received");
                    break;
                }
                _ = tokio::time::sleep(tokio::time::Duration::from_millis(500)) => {
                }
            }

            if Instant::now() > deadline {
                break;
            }
        }
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_generate_secp256k1_keypair() {
        let (priv_key, public_key) = generate_secp256k1_keypair();
        assert!(!priv_key[..].is_empty());
        assert!(!public_key.serialize()[..].is_empty());

        let secp = Secp256k1::new();

        // verify that the public key is derived from the secret key
        assert_eq!(PublicKey::from_secret_key(&secp, &priv_key), public_key);
    }

    #[test]
    fn generate_secp256k1_keypair_unique_key_pairs_generated() {
        let (secret_key1, public_key1) = generate_secp256k1_keypair();
        let (secret_key2, public_key2) = generate_secp256k1_keypair();

        // verify that the generated secret keys and public keys are unique
        assert_ne!(
            secret_key1, secret_key2,
            "Secret keys should be randomly generated"
        );
        assert_ne!(
            public_key1, public_key2,
            "Public keys should be randomly generated"
        );
    }

    #[test]
    fn test_local_time() {
        let timestamp = 1735660800; // 2025-1-1 00:00:00 Asia/Shanghai UTC in Unix timestamp;
        let local_time = seconds_to_local_time(timestamp);
        println!("Local time: {}", local_time);
        assert_eq!(local_time, "2025-01-01 00:00:00");
    }
}
